// -----------------------------------------------------------------------
// <copyright file="Incremental.cs">
// Original Triangle code by Jonathan Richard Shewchuk, http://www.cs.cmu.edu/~quake/triangle.html
// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
// </copyright>
// -----------------------------------------------------------------------

namespace UnityEngine.U2D.Animation.TriangleNet
    .Meshing.Algorithm
{
    using System.Collections.Generic;
    using Animation.TriangleNet.Topology;
    using Animation.TriangleNet.Geometry;

    /// <summary>
    /// Builds a delaunay triangulation using the incremental algorithm.
    /// </summary>
    internal class Incremental : ITriangulator
    {
        Mesh mesh;

        /// <summary>
        /// Form a Delaunay triangulation by incrementally inserting vertices.
        /// </summary>
        /// <returns>Returns the number of edges on the convex hull of the
        /// triangulation.</returns>
        public IMesh Triangulate(IList<Vertex> points, Configuration config)
        {
            this.mesh = new Mesh(config);
            this.mesh.TransferNodes(points);

            Otri starttri = new Otri();

            // Create a triangular bounding box.
            GetBoundingBox();

            foreach (var v in mesh.vertices.Values)
            {
                starttri.tri = mesh.dummytri;
                Osub tmp = default(Osub);
                if (mesh.InsertVertex(v, ref starttri, ref tmp, false, false) == InsertVertexResult.Duplicate)
                {
                    if (Log.Verbose)
                    {
                        Log.Instance.Warning("A duplicate vertex appeared and was ignored.",
                            "Incremental.Triangulate()");
                    }
                    v.type = VertexType.UndeadVertex;
                    mesh.undeads++;
                }
            }

            // Remove the bounding box.
            this.mesh.hullsize = RemoveBox();

            return this.mesh;
        }

        /// <summary>
        /// Form an "infinite" bounding triangle to insert vertices into.
        /// </summary>
        /// <remarks>
        /// The vertices at "infinity" are assigned finite coordinates, which are
        /// used by the point location routines, but (mostly) ignored by the
        /// Delaunay edge flip routines.
        /// </remarks>
        void GetBoundingBox()
        {
            Otri inftri = default(Otri); // Handle for the triangular bounding box.
            Rectangle box = mesh.bounds;

            // Find the width (or height, whichever is larger) of the triangulation.
            double width = box.Width;
            if (box.Height > width)
            {
                width = box.Height;
            }
            if (width == 0.0)
            {
                width = 1.0;
            }
            // Create the vertices of the bounding box.
            mesh.infvertex1 = new Vertex(box.Left - 50.0 * width, box.Bottom - 40.0 * width);
            mesh.infvertex2 = new Vertex(box.Right + 50.0 * width, box.Bottom - 40.0 * width);
            mesh.infvertex3 = new Vertex(0.5 * (box.Left + box.Right), box.Top + 60.0 * width);

            // Create the bounding box.
            mesh.MakeTriangle(ref inftri);

            inftri.SetOrg(mesh.infvertex1);
            inftri.SetDest(mesh.infvertex2);
            inftri.SetApex(mesh.infvertex3);

            // Link dummytri to the bounding box so we can always find an
            // edge to begin searching (point location) from.
            mesh.dummytri.neighbors[0] = inftri;
        }

        /// <summary>
        /// Remove the "infinite" bounding triangle, setting boundary markers as appropriate.
        /// </summary>
        /// <returns>Returns the number of edges on the convex hull of the triangulation.</returns>
        /// <remarks>
        /// The triangular bounding box has three boundary triangles (one for each
        /// side of the bounding box), and a bunch of triangles fanning out from
        /// the three bounding box vertices (one triangle for each edge of the
        /// convex hull of the inner mesh).  This routine removes these triangles.
        /// </remarks>
        int RemoveBox()
        {
            Otri deadtriangle = default(Otri);
            Otri searchedge = default(Otri);
            Otri checkedge = default(Otri);
            Otri nextedge = default(Otri), finaledge = default(Otri), dissolveedge = default(Otri);
            Vertex markorg;
            int hullsize;

            bool noPoly = !mesh.behavior.Poly;

            // Find a boundary triangle.
            nextedge.tri = mesh.dummytri;
            nextedge.orient = 0;
            nextedge.Sym();

            // Mark a place to stop.
            nextedge.Lprev(ref finaledge);
            nextedge.Lnext();
            nextedge.Sym();
            // Find a triangle (on the boundary of the vertex set) that isn't
            // a bounding box triangle.
            nextedge.Lprev(ref searchedge);
            searchedge.Sym();
            // Check whether nextedge is another boundary triangle
            // adjacent to the first one.
            nextedge.Lnext(ref checkedge);
            checkedge.Sym();
            if (checkedge.tri.id == Mesh.DUMMY)
            {
                // Go on to the next triangle.  There are only three boundary
                // triangles, and this next triangle cannot be the third one,
                // so it's safe to stop here.
                searchedge.Lprev();
                searchedge.Sym();
            }

            // Find a new boundary edge to search from, as the current search
            // edge lies on a bounding box triangle and will be deleted.
            mesh.dummytri.neighbors[0] = searchedge;

            hullsize = -2;
            while (!nextedge.Equals(finaledge))
            {
                hullsize++;
                nextedge.Lprev(ref dissolveedge);
                dissolveedge.Sym();
                // If not using a PSLG, the vertices should be marked now.
                // (If using a PSLG, markhull() will do the job.)
                if (noPoly)
                {
                    // Be careful!  One must check for the case where all the input
                    // vertices are collinear, and thus all the triangles are part of
                    // the bounding box.  Otherwise, the setvertexmark() call below
                    // will cause a bad pointer reference.
                    if (dissolveedge.tri.id != Mesh.DUMMY)
                    {
                        markorg = dissolveedge.Org();
                        if (markorg.label == 0)
                        {
                            markorg.label = 1;
                        }
                    }
                }
                // Disconnect the bounding box triangle from the mesh triangle.
                dissolveedge.Dissolve(mesh.dummytri);
                nextedge.Lnext(ref deadtriangle);
                deadtriangle.Sym(ref nextedge);
                // Get rid of the bounding box triangle.
                mesh.TriangleDealloc(deadtriangle.tri);
                // Do we need to turn the corner?
                if (nextedge.tri.id == Mesh.DUMMY)
                {
                    // Turn the corner.
                    dissolveedge.Copy(ref nextedge);
                }
            }

            mesh.TriangleDealloc(finaledge.tri);

            return hullsize;
        }
    }
}
